# -*- coding: utf-8 -*-

"""
Created on Fri Jun  8 18:43:24 2018

@author: Administrator
"""
#%%
#此脚本档用在linux系统上时，需要确认是否使用‘GBK’解码，可能是用‘utf-8’
import sys
import os
import shutil
import glob
import subprocess
from agilepipe.log import Log_make

#---------------------------------以下内容为错误信息print输出
logmake = Log_make()

def info_print(paragr):
    
    #作用原理：输出程序的运行信息
    #参数说明：paragr是一个双重嵌套list即：paragr[i] 代表要输出的第i行，而paragr[i][j]代表要输出的第i行的第j个元素。
    #返回值说明：无返回值
    #举例：line1 = ['','>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>']；line2 = ['Number Of dir_file： ',i]
    #举例：用上面的line1 line2来构造paragr = [line1,line2]
    #调用：
    #被调用：dircp_info_print()   err_info_print()   succ_info_print()
    #bug：
    for i in range(0,len(paragr)):
        for j in range(0,len(paragr[i])):
            print(paragr[i][j],end='')
        print('')


def dircp_info_print(i,k,item):
    
    #作用原理：输出复制文件夹的信息
    #参数说明：
    #参数说明：
    #返回值说明：无返回值
    #举例：
    #调用：inro_print()
    #被调用：unpack_and_copy()
    #bug：
    line1 = ['','>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>']
    line2 = ['Number Of dir_file： ',i]
    line3 = ['Number of Dircp: ',k]
    line4 = ['Do NOt Need Extract: ',item]
    line5 = ['\n' * 2]
    paragr = [line1,line2,line3,line4,line5]
    info_print(paragr)
    

def err_info_print(i,j,item,stdout_err):
    #作用原理：输出成解压报错信息
    #参数说明：
    #参数说明：
    #返回值说明：无返回值
    #举例：
    #调用：inro_print()
    #被调用：unpack_and_copy()
    #bug：
    code_standard = logmake.get_code_standard()
    line1 = ['','@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@']
    line2 = ['Number Of dir_file： ',i]
    line3 = ['Number of Error: ',j]
    line4 = ['Extract Failure: ',item]
    line5 = ['stdout: \n', stdout_err[0].decode(code_standard)]
    line6 = ['stderr: \n', stdout_err[1].decode(code_standard)]
    line7 = ['\n' * 2]
    paragr = [line1,line2,line3,line4,line5,line6,line7]
    info_print(paragr)


def succ_info_print(i,l,item):
    #作用原理：输出成功解压的信息
    #参数说明：
    #参数说明：
    #返回值说明：无返回值
    #举例：
    #调用：inro_print()
    #被调用：unpack_and_copy()
    #bug：
    line1 = ['','!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!']
    line2 = ['Number Of dir_file： ',i]
    line3 = ['Number of Archive: ',l]
    line4 = ['Extract Success: ',item]
    line5 = ['\n' * 2]
    paragr = [line1,line2,line3,line4,line5]
    info_print(paragr)


def search_dirs(start_path,level_need):
    
    #作用：生成满足条件的所有文件路径。
    #参数说明：start_path 跟调用次函数的同名变量意义一致
    #参数说明：level_need 跟调用次函数的同名变量意义一致
    #返回值说明：
    #举例：
    #调用：glob模块
    #被调用：unpack_and_copy
    level = (os.sep + '*') * level_need
    search_res = glob.glob(start_path + level)
    return search_res

def sevenzip_unpack(file_path,outp_path,sevenzip_path):
    
    #作用：使用subprocess.Popen()调用shell（linux）、cmd（win7）中的7z命令（需已经安装7z软件，且将7z.exe放在环境变量中），来实现对file_path解压缩的功能。
    #作用：在windows平台上7z的cmd命令是：7z x H:\data\huxianzhang最原始文件\01\after\"HY_072_2_CS (G).zip"  -oH:\data\HuXianZhang
    #作用：因为HY_072_2_CS (G).zip这个文件名子中间含有空格，会导致报错，所以需要加双引号（space_in_path()函数就是此目的）
    #作用：其余的如data、huixianzhang最原始文件、after等文件夹名字，不含有空格，所以可以加双引号、也可以不加双引号
    #参数说明：file_path是需要解压缩的文件的完整路径（包括文件名）
    #参数说明：outp_path是加压缩后文件的存放路径
    #参数说明：sevenzip_path，是7z命令的完整路径，是预留变量，暂时还没有用到。目的是在linux中，即使7z不在环境变量中，也可以在本程序中临时设置环境变量，
    #参数说明：--通过完整路径来调用7z。
    #返回值说明：
    #举例：sevenzip_unpack(r'H:\data\huxianzhang最原始文件',r'H:\data\HuXianZhang',r'')
    #调用：subprocess模块  space_in_path()
    #被调用：unpack_and_copy()
    quation = '"'
    print('file_path:',file_path)
    print('outp_path:',outp_path)
#    cmd = '7z  x ' + space_in_path(file_path) + r' -o' + space_in_path(outp_path)
    cmd = '7z  x ' + quation + file_path + quation + r' -o' + quation + outp_path + quation
    print(cmd)
    popen_subj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    stdout_err = popen_subj.communicate()
    return stdout_err

def rar_unpack(file_path,outp_path,sevenzip_path):
    #the useness of this function is same to  sevenzip_unpack,except that unrar command is used to uncompress the file of rar format
    quation = '"'
    space = ' '
    print('file_path:',file_path)
    print('outp_path:',outp_path)
    cmd = 'unrar x ' + quation + file_path + quation + space + quation + outp_path + os.sep + quation 
    print(cmd)
    popen_subj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    stdout_err = popen_subj.communicate()
    return stdout_err
    pass

def dir_copy(inp_path,outp_path):
        
    #作用：将inp_path文件夹中的所有内容都复制到outp_path中
    #参数说明：inp_path是一个文件夹的路径
    #参数说明：outp_path是目的路径
    #返回值说明：无
    #举例：dir_copy(r'H:\data\YangHong最原始文件\2017',r'H:\data\YangHong')
    #调用：shutil模块
    #被调用：unpack_and_copy()
    if os.path.exists(outp_path):
        print('the file exists:',outp_path)
        return
    print('copying file:',)
    shutil.copytree(inp_path,outp_path)

    
def file_copy(inp_path,outp_path):
    
    #作用：将inp_path文件复制到outp_path中
    #参数说明：inp_path是一个完整的文件路径（包括文件名）
    #参数说明：outp_path是目的路径
    #返回值说明：无
    #举例：file_copy(r'H:\data\huxianzhang最原始文件\01\afterHY_072_2_CS (G).zip',r'H:\data\HuXianZhang\01\after')
    #调用：shutil模块
    #被调用：unpack_and_copy()
    outp_filepath = outp_path+os.sep+os.path.split(inp_path)[1]
    if not os.path.exists(outp_path):
        os.makedirs(outp_path)
    if os.path.exists(outp_filepath):
        print('the file exists:',outp_filepath)
        return
    shutil.copy(inp_path,outp_path)
	
#%%
#有些原始数据是将一个subject的所有modality都压缩到一个文件夹里，MRIConvert不能处理这些压缩文件，所以本软件的目的就是为了将
#这些压缩文件解压到同名文件夹下，并且复制到与原来文件路径一致的文件夹中（即，保留原来的文件路径结构）
def unpack_and_copy(start_path,level_need,sevenzip_path=''):
    #作用：批量解压缩MRI数据(format':'.zip','.7z','.gz','.tar','.rar')，为使用MRIConvert做准备:
    #参数说明：start_path是指所有压缩文件的共有目录，level_need是指压缩文件在共有目录之间的层级数
    #参数说明：sevenzip_path表示7z的路径，但win7批处理脚本很难写，所以在win7上应直接把7z的路径设为环境变量
    #返回值说明：
    #举例：如果需要解压缩的文件为H:\data\huxianzhang最原始文件\01\after\HY_072_2_CS (G).zip则：
    #举例：unpack_and_copy(r'H:\data\huxianzhang最原始文件',3,r'H:\data\HuXianZhang',)
    #调用：
    #被调用：sevenzip_unpack()   err_log()
    
    #参数准备
    i = 0; k = 0; j = 0; l = 0
    #outp_path必须是H:\data\huxianzhang最原始文件_ready4d2n(ready for dcm to nii)这个文件夹，所以无需作为函数参数输入。
    #其他的MRIConvert、dcm2nii等dicom至nii转换软件都是在\data\huxianzhang最原始文件_ready4d2n基础上进行的。
    last_name = os.path.split(start_path)[1]
    outp_path = start_path.replace(last_name,last_name + '_ready4d2n')
    search_res = search_dirs(start_path,level_need)
    print('search_res:',search_res)
    sevenzip_suffix = ['.zip','.7z','.gz','.tar']
    rar_suffix = ['.rar']
    for item in search_res:
        i = i + 1
        medi_path = item.replace(start_path,outp_path)
        medi_path_shallow = os.path.split(medi_path)[0]
        medi_path_deep = os.path.splitext(medi_path)[0]
        dircp_log_path = medi_path_shallow + os.sep + sys._getframe().f_code.co_name + '_dircp_log.txt'
        err_log_path = dircp_log_path.replace('_dircp_log.txt','_err_log.txt')
        #if the item is dirpath then copy it to the outp_path
        if os.path.isdir(item):
            k = k + 1
            dir_copy(item,medi_path)
            logmake.python_err_log_write(dircp_log_path,item,i,k)
            dircp_info_print(i,k,item)
        #if the item is a file  then according to the file suffix ,either 7z or unrar is used to uncompress the compressed file.
        if os.path.isfile(item):
            #the expression below is to get the suffix of the file
            compress = os.path.splitext(item)[1].lower()
            if  compress in sevenzip_suffix:
                stdout_err = sevenzip_unpack(item,medi_path_deep,sevenzip_path)
            elif compress in rar_suffix:
                stdout_err = rar_unpack(item,medi_path_deep,sevenzip_path)
            else:
                print("plese check the suffix of the compressed file")
                return
            #stdout_err是一个list，stdout_err[0]含有stdout输出，stdout_err[1]含有stderr输出。
            if stdout_err[1] != b'':
                j = j + 1
                #解压缩失败的情况下（1.压缩文件自身问题解压缩失败;2.非压缩文件导致解压缩失败），将未成功的文件拷贝
                #到medi_path_shallow里，然后继续加压缩下一个文件。
                file_copy(item,medi_path_shallow)
                #将发生错误的信息写到中log_path中
                logmake.shell_err_log_write(err_log_path,item,stdout_err,i,j)
                #输出解压缩失败的提示信息
                err_info_print(i,j,item,stdout_err)
            else:
                l = l + 1
                succ_info_print(i,l,item)
    return

#%%
if __name__ == '__main__':
#    unpack_and_copy(r'/media/root/Elements3/data_1',3)
    unpack_and_copy(r'/media/root/Elements3/data_1',1)
#3 should before 1